﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using log4net;
using TeamCommunitySolution.Domain.DataLayer.Venues;
using TeamCommunitySolution.Modules.Shared;
using TeamCommunitySolution.Modules.Venues.Requests;
using TeamCommunitySolution.Modules.Venues.Responses;
using TeamCommunitySolution.Repositories.Base;
using TeamCommunitySolution.Repositories.Venues;

namespace TeamCommunitySolution.Modules.Venues
{
    public class VenueManagerImpl : VenueManager
    {
        /// <summary>
        /// The logger instance.
        /// </summary>
        private static readonly ILog _log = LogManager.GetLogger(typeof(VenueManagerImpl));

        /// <summary>
        /// The repository reference.
        /// </summary>
        private VenueRepository venueRepo;

        /// <summary>
        /// Prevents a default instance of the <see cref="VenueManagerImpl"/> class from being created.
        /// </summary>
        private VenueManagerImpl()
        {
        }

        /// <summary>
        /// Creates the instance.
        /// </summary>
        /// <param name="venueRepository">The venue repository.</param>
        /// <returns>An instance of a venue manager.</returns>
        public static VenueManager CreateInstance(VenueRepository venueRepository)
        {
            VenueManagerImpl output = new VenueManagerImpl();
            output.venueRepo = venueRepository;
            return output;
        }

        /// <summary>
        /// Creates the venue.
        /// </summary>
        /// <param name="venueRequest">The venue request.</param>
        /// <returns>Venue Reponse Object.</returns>
        public VenueResponse CreateVenue(VenueRequest venueRequest)
        {
            ValidatePreconditions(venueRequest);

            VenueResponse response = new VenueResponse();

            try
            {
                Venue venue = new Venue
                {
                    DateFounded = venueRequest.DateFounded,
                    Name = venueRequest.VenueName,
                    Owner = venueRequest.Owner,
                    Description = venueRequest.Description,
                    Latitude = venueRequest.Latitude,
                    Longditude = venueRequest.Longditude,
                    Website = venueRequest.Website,
                };

                this.venueRepo.SaveVenue(venue);

                response.Venue = venue;
                response.Status = OperationStatus.Success;
            }
            catch (BaseRepositoryException e)
            {
                response.Status = OperationStatus.Failure;
                response.Message = string.Format("Incomplete venue creation for request {0} : {1}", venueRequest.VenueName, e.Message);
                response.Exception = e;
                _log.Error(response.Message, e);
            }

            return response;
        }

        /// <summary>
        /// Updates the venue.
        /// </summary>
        /// <param name="updateRequest">The update request.</param>
        /// <returns>Venue Response.</returns>
        public VenueResponse UpdateVenue(VenueRequest updateRequest)
        {
            ValidatePreconditions(updateRequest);

            if (string.IsNullOrEmpty(updateRequest.Id))
            {
                throw new InvalidVenueRequest("The update request requires an id");
            }

            VenueResponse response = new VenueResponse();

            try
            {
                Venue venue = new Venue
                {
                    DateFounded = updateRequest.DateFounded,
                    Name = updateRequest.VenueName,
                    Owner = updateRequest.Owner,
                    Description = updateRequest.Description,
                    Latitude = updateRequest.Latitude,
                    Longditude = updateRequest.Longditude,
                    Website = updateRequest.Website,
                    Id = updateRequest.Id
                };

                this.venueRepo.SaveVenue(venue);

                response.Venue = venue;
                response.Status = OperationStatus.Success;
            }
            catch (BaseRepositoryException e)
            {
                response.Status = OperationStatus.Failure;
                response.Message = string.Format("Incomplete venue update for request {0} : {1}", updateRequest.VenueName, e.Message);
                response.Exception = e;
                _log.Error(response.Message, e);
            }

            return response;
        }

        /// <summary>
        /// Deletes the venue.
        /// </summary>
        /// <param name="deleteRequest">The delete request.</param>
        /// <returns>Venue Response.</returns>
        public VenueResponse DeleteVenue(VenueRequest deleteRequest)
        {
            if (string.IsNullOrEmpty(deleteRequest.Id))
            {
                throw new InvalidVenueRequest("The delete request requires an id");
            }

            VenueResponse response = new VenueResponse();

            try
            {
                Venue venue = new Venue
                {
                    Id = deleteRequest.Id
                };

                this.venueRepo.DeleteVenue(venue);

                response.Venue = venue;
                response.Status = OperationStatus.Success;
            }
            catch (BaseRepositoryException e)
            {
                response.Status = OperationStatus.Failure;
                response.Message = string.Format("Incomplete venue delete for request {0} : {1}", deleteRequest.Id, e.Message);
                response.Exception = e;
                _log.Error(response.Message, e);
            }

            return response;
        }

        /// <summary>
        /// Gets the venue by id.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns>Venue Response.</returns>
        public VenueResponse GetVenueById(VenueRequest request)
        {
            if (string.IsNullOrEmpty(request.Id))
            {
                throw new InvalidVenueRequest("The lookup request requires an id");
            }

            VenueResponse response = new VenueResponse();

            try
            {
                response.Venue = this.venueRepo.GetVenueByField("Id", request.Id);
                response.Status = OperationStatus.Success;
            }
            catch (BaseRepositoryException e)
            {
                response.Status = OperationStatus.Failure;
                response.Message = string.Format("Incomplete venue lookup for request {0} : {1}", request.Id, e.Message);
                response.Exception = e;
                _log.Error(response.Message, e);
            }

            return response;
        }

        /// <summary>
        /// Validates the preconditions.
        /// </summary>
        /// <param name="venueRequest">The venue request.</param>
        private void ValidatePreconditions(VenueRequest venueRequest)
        {
            if (venueRequest == null)
            {
                throw new InvalidVenueRequest("The venue request cannot be null");
            }

            if (String.IsNullOrEmpty(venueRequest.VenueName))
            {
                throw new InvalidVenueRequest("The venue name cannot be null or empty");
            }

            if (String.IsNullOrEmpty(venueRequest.Description))
            {
                throw new InvalidVenueRequest("The venue description cannot be null or empty");
            }

            if (venueRequest.Owner == null)
            {
                throw new InvalidVenueRequest("The venue owner cannot be null.");
            }

            if (BaseRepositoryImpl.CreateInstance().GetUserByField("Id", venueRequest.Owner.Id) == null)
            {
                throw new InvalidVenueRequest("The venue owner specified does not exist.");
            }

            if (venueRequest.DateFounded.Equals(DateTime.MinValue))
            {
                venueRequest.DateFounded = DateTime.Now;
            }
        }
    }
}
